#pragma once
#include "Log.hpp"
#include <mutex>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>

class TcpServer
{
private:
  TcpServer(int port)
      : _port(port), _listen_sock(-1)
  {
  }
  TcpServer(const TcpServer &ts) = delete;
  TcpServer &operator=(const TcpServer &ts) = delete;

public:
  ~TcpServer()
  {
    if (_listen_sock > 0)
      close(_listen_sock);
  };

  static TcpServer *GetInstance(int port);
  void InitServer();
  void Socket();
  void Bind();
  void Listen();
  int GetSock() { return _listen_sock; }

private:
  int _port;
  int _listen_sock;
  static TcpServer *_svr;
};
TcpServer *TcpServer::_svr = nullptr;

TcpServer *TcpServer::GetInstance(int port)
{
  static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
  pthread_mutex_lock(&lock);
  if (_svr == nullptr)
  {
    _svr = new TcpServer(port);
    _svr->InitServer();
  }
  LOG(INFO, "get tcp server instance success");
  pthread_mutex_unlock(&lock);
  return _svr;
}

void TcpServer::InitServer()
{
  Socket();
  Bind();
  Listen();
  LOG(INFO, "TcpServer init success");
}

void TcpServer::Socket()
{
  _listen_sock = socket(AF_INET, SOCK_STREAM, 0);
  if (_listen_sock < 0)
  {
    // std::cerr << "socket errno" << errno << std::cout;
    LOG(FATAL, "socket errno");
    exit(1);
  }
  LOG(INFO, "socket success");
  // std::cout << "socket success" << std::endl;
  // socket地址复用
  // 若服务器崩了很容易出现端口(port)被占用的情况，导致
  // 服务器无法立即重启, 使用此方法可以让服务器立即重启

  int opt = 1;
  setsockopt(_listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
}

void TcpServer::Bind()
{
  struct sockaddr_in local;
  memset(&local, 0, sizeof(local));
  local.sin_family = AF_INET;
  local.sin_port = htons(_port);
  // 云服务器的公网IP是虚拟出来的，不可直接绑定
  local.sin_addr.s_addr = INADDR_ANY;
  if (bind(_listen_sock, (struct sockaddr *)&local, sizeof(local)) < 0)
  {
    // std::cerr << "bind errno" << errno << std::endl;
    LOG(FATAL, "bind errno");
    exit(2);
  }
  // std::cout << "bind success" << std::endl;
  LOG(INFO, "bind success");
}

void TcpServer::Listen()
{
  if (listen(_listen_sock, 5) < 0)
  {
    // std::cerr << "listen errno" << errno << std::endl;
    LOG(FATAL, "listen errno");
    exit(3);
  }
  // std::cout << "listen success" << std::endl;
  LOG(INFO, "listen success");
}
